﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX;

namespace System.Windows.Media.Media3D10
{
    /// <summary>
    /// Permet l'assignation des informations relatives à la caméra dans l'effet associé
    /// </summary>
    public sealed class CameraPositionEffectParameter : EffectParameter
    {
        #region Dependency Properties
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ApplyLocalPosition"/>.
        /// </summary>
        public static readonly DependencyProperty ApplyLocalPositionProperty =
            DependencyProperty.Register("ApplyLocalPosition", typeof(bool), typeof(CameraPositionEffectParameter));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ApplyWorldPosition"/>.
        /// </summary>
        public static readonly DependencyProperty ApplyWorldMatrixProperty =
            DependencyProperty.Register("ApplyWorldPosition", typeof(bool), typeof(CameraPositionEffectParameter));
        #endregion

        #region Data
        private const string g_vCameraPositionLocal = "g_vCameraPositionLocal";
        private const string g_vCameraPositionWorld = "g_vCameraPositionWorld";

        private SlimDX.Direct3D10.EffectVectorVariable _cameraPositionLocal;
        private SlimDX.Direct3D10.EffectVectorVariable _cameraPositionWorld;
        #endregion

        #region Public Properties
        /// <summary>
        /// Obient ou définit une valeur indiquant si la position dans l'espace objet doit être définie
        /// </summary>
        public bool ApplyLocalPosition
        {
            get { return (bool)GetValue(ApplyLocalPositionProperty); }
            set { SetValue(ApplyLocalPositionProperty, value); }
        }

        /// <summary>
        /// Obient ou définit une valeur indiquant si la position dans l'espace monde doit être définie
        /// </summary>
        public bool ApplyWorldPosition
        {
            get { return (bool)GetValue(ApplyWorldMatrixProperty); }
            set { SetValue(ApplyWorldMatrixProperty, value); }
        }
        #endregion

        #region EffectParameter Implementation
        /// <summary>
        /// Appelé lors de l'initialisation de l'effet associé
        /// </summary>
        /// <param name="effect">Effet associé</param>
        protected override void OnInitEffect(SlimDX.Direct3D10.Effect effect)
        {
            base.OnInitEffect(effect);
            if (effect != null)
            {
                _cameraPositionLocal = effect.GetVariableByName(g_vCameraPositionLocal).AsVector();
                _cameraPositionWorld = effect.GetVariableByName(g_vCameraPositionWorld).AsVector();
            }
            else
            {
                _cameraPositionLocal = null;
                _cameraPositionWorld = null;
            }
        }

        /// <summary>
        /// En cas de substitution dans une classe dérivée, participe au dessin des objets dans le pipeline DirectX10
        /// </summary>
        /// <param name="context">Contexte de dessin.</param>
        /// <exception cref="ArgumentNullException">Soulevée si le contexte est null.</exception>
        protected override void OnDraw(DrawingContext context)
        {
            if (object.ReferenceEquals(context, null))
                throw new ArgumentNullException("context");

            if (ApplyLocalPosition || ApplyWorldPosition)
            {
                Vector3 worldPosition = context.CameraPosition;
                if (this.ApplyLocalPosition)
                {
                    Vector3 objectPosition = Vector3.TransformCoordinate(Vector3.Zero, context.WorldMatrix);
                    Vector3 localPosition = worldPosition - objectPosition;
                    _cameraPositionLocal.Set(localPosition);
                }
                if (this.ApplyWorldPosition)
                {
                    _cameraPositionWorld.Set(worldPosition);
                }
            }
        }
        #endregion

        #region Freezable Implementation
        /// <summary>
        /// Une fois implémentée dans une classe dérivée, initialise une nouvelle instance de la classe dérivée <see cref="Freezable"/>. 
        /// </summary>
        /// <returns>Nouvelle instance.</returns>
        protected override Freezable CreateInstanceCore()
        {
            return new CameraPositionEffectParameter();
        }
        #endregion
    }
}
